﻿using DeviceFaultPushService.DeviceProcessManagement.CommunicationManagement.Models;
using DeviceFaultPushService.DeviceProcessManagement.CommunicationManagement.SiemensS7.Models;
using DeviceFaultPushService.MessagePushService.Models;
using S7.Net;
using S7.Net.Types;
using DeviceInformationModel = DeviceFaultPushService.DeviceProcessManagement.CommunicationManagement.SiemensS7.Models.DeviceInformationModel;

namespace DeviceFaultPushService.DeviceProcessManagement.CommunicationManagement.SiemensS7;

public class DeviceCommunication(DeviceInformationModel deviceInformation) : IDeviceCommunication
{
    private Plc plc;
    private DeviceInformationModel device = deviceInformation;

    public bool IsConnected => plc.IsConnected;
    public string Name => device.Name;

    public List<ContactsModel> Contacts => device.Contacts;

    public string Id => device.Number;

    public void Connect()
    {
        try
        {
            DeviceAddressModel deviceAddress = device.DeviceAddress;
            //检查地址有效性
            ConnectionCheck connectionCheck = new();
            connectionCheck.IsIpEffective(deviceAddress.Ip);

            plc = new(deviceAddress.CpuType, deviceAddress.Ip, deviceAddress.Port, deviceAddress.Rack, deviceAddress.Slot);

            plc.Open();
        }
        catch (Exception ex)
        {
            plc = null;
            throw new Exception(ex.Message);
        }
    }

    //报警结果列表
    List<FaultDataInformationModel> results = null;
    //分页数据
    private List<List<DataItem>> pages = null;

    public void Read(out List<FaultDataInformationModel> result)
    {
        //检查报警地址是否存在
        if (results is null)
        {
            //数据地址检查
            if (device.DataAddress is null || device.DataAddress.Count < 1)
            {
                results = null;
                result = results;
                return;
            }
            results = new();
            foreach (var item in device.DataAddress)
            {
                //未启用报警，则不添加
                if (item.IsEnable == false)
                {
                    continue;
                }
                results.Add(new FaultDataInformationModel()
                {
                    Addr = item.AlarmAddress,
                    Uid = Guid.NewGuid().ToString("N"),
                    Message = item.AlarmInformation,
                });
            }
        }
        //是否批量读取
        if (device.DeviceAddress.IsMultiple)
        {
            if (pages is null)
            {
                //获取数据地址
                List<DataItem> data = new List<DataItem>();

                foreach (var item in results)
                {
                    data.Add(DataItem.FromAddress(item.Addr));
                }
                // 将一维列表按长度分页   单纯最大数据量 16
                pages = PaginateList(data, 16);
            }
            // 输出分页结果  
            foreach (var page in pages)
            {
                //批量读取
                plc.ReadMultipleVars(page);
            }
            //列表转换
            List<DataItem> oneDimensionalList = pages.SelectMany(list => list).ToList();
            //遍历结果
            for (int i = 0; i < oneDimensionalList.Count; i++)
            {
                results[i].Value = (bool)oneDimensionalList[i].Value;
            }
        }
        else
        {
            foreach (var item in results)
            {
                item.Value = (bool)plc.Read(item.Addr);
                Thread.Sleep(1);
            }
        }
        result = results;
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <param name="list"></param>
    /// <param name="pageSize"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    static List<List<T>> PaginateList<T>(List<T> list, int pageSize)
    {
        return list
            .Select((item, index) => new { item, index })
            .GroupBy(x => x.index / pageSize)
            .Select(x => x.Select(v => v.item).ToList())
            .ToList();
    }

    public void Close()
    {
        if (plc is not null)
        {
            plc.Close();
        }
    }
}